package cn.turboinfo.fuyang.api.provider.common.service.impl.order;

import cn.turboinfo.fuyang.api.domain.common.service.audit.CreditRatingAuditRecordService;
import cn.turboinfo.fuyang.api.domain.common.service.credit.CreditRatingService;
import cn.turboinfo.fuyang.api.domain.common.service.order.PayOrderService;
import cn.turboinfo.fuyang.api.domain.common.service.order.RefundOrderService;
import cn.turboinfo.fuyang.api.domain.common.service.order.ServiceOrderService;
import cn.turboinfo.fuyang.api.domain.common.service.wechat.WechatPayService;
import cn.turboinfo.fuyang.api.domain.util.CurrencyUnitUtils;
import cn.turboinfo.fuyang.api.entity.common.enumeration.audit.AuditStatus;
import cn.turboinfo.fuyang.api.entity.common.enumeration.common.EntityObjectType;
import cn.turboinfo.fuyang.api.entity.common.enumeration.credit.CreditRatingStatus;
import cn.turboinfo.fuyang.api.entity.common.enumeration.order.*;
import cn.turboinfo.fuyang.api.entity.common.pojo.audit.CreditRatingAuditRecordCreator;
import cn.turboinfo.fuyang.api.entity.common.pojo.credit.CreditRatingCreator;
import cn.turboinfo.fuyang.api.entity.common.pojo.order.*;
import cn.turboinfo.fuyang.api.provider.common.repository.database.order.ServiceOrderDAO;
import cn.turboinfo.fuyang.api.provider.common.repository.database.order.ServiceOrderPO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import net.sunshow.toolkit.core.qbean.helper.component.request.QBeanCreatorHelper;
import net.sunshow.toolkit.core.qbean.helper.service.impl.DefaultQServiceImpl;
import nxcloud.foundation.core.data.support.annotation.EnableSoftDelete;
import nxcloud.foundation.core.idgenerator.IdGeneratorFacade;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;

@Slf4j
@RequiredArgsConstructor
@EnableSoftDelete
@Service
public class ServiceOrderServiceImpl extends DefaultQServiceImpl<ServiceOrder, Long, ServiceOrderPO, ServiceOrderDAO> implements ServiceOrderService {

    private final PayOrderService payOrderService;

    private final RefundOrderService refundOrderService;

    private final CreditRatingService creditRatingService;

    private final WechatPayService wechatPayService;

    private final IdGeneratorFacade<Long> idGeneratorFacade;

    private final CreditRatingAuditRecordService creditRatingAuditRecordService;

    @Override
    @Transactional
    public ServiceOrder save(ServiceOrderCreator creator) {
        ServiceOrderPO serviceOrderPO = new ServiceOrderPO();
        QBeanCreatorHelper.copyCreatorField(serviceOrderPO, creator);

        // 手动生成ID
        serviceOrderPO.setId(idGeneratorFacade.nextId());
        return convertQBean(dao.save(serviceOrderPO));
    }


    @Override
    public List<ServiceOrder> findByStaffId(Long staffId, ServiceOrderStatus status) {
        return convertQBeanToList(dao.findByStaffIdAndOrderStatusOrderByCreatedTimeDesc(staffId, status));
    }

    @Override
    public List<ServiceOrder> findByCompanyId(Long companyId, ServiceOrderStatus status) {
        return convertQBeanToList(dao.findByCompanyIdAndOrderStatusOrderByCreatedTimeDesc(companyId, status));
    }

    @Override
    public List<ServiceOrder> findByCompanyId(Long companyId, Collection<ServiceOrderStatus> statusCollection) {
        return convertQBeanToList(dao.findByCompanyIdAndOrderStatusInOrderByCreatedTimeDesc(companyId, statusCollection));
    }

    @Override
    public List<ServiceOrder> findByShopId(Long shopId, ServiceOrderStatus status) {
        return convertQBeanToList(dao.findByShopIdAndOrderStatusOrderByCreatedTimeDesc(shopId, status));
    }

    @Override
    public List<ServiceOrder> findByProductId(Long productId, ServiceOrderStatus status) {
        return convertQBeanToList(dao.findByProductIdAndOrderStatusOrderByCreatedTimeDesc(productId, status));
    }

    @Transactional
    @Override
    public void submitOrderPostPaid(Long serviceOrderId, Long payOrderId) {
        ServiceOrderPO po = getEntityWithNullCheckForUpdate(serviceOrderId);

        if (po.getOrderStatus() != ServiceOrderStatus.INIT) {
            throw new IllegalArgumentException("订单状态不正确, 不能标记为后付费");
        }

        po.setPayStatus(ServiceOrderPayStatus.POST_PAID);
        // 因为不是真支付了, 不设置支付时间

        payOrderService.checkAndUpdateStatus(payOrderId, PayOrderStatus.POST_PAID, PayOrderStatus.UNPAID);

        transferStatusAfterPaid(po);
    }

    @Override
    @Transactional
    public void submitOrderPaid(Long serviceOrderId, Long payOrderId, BigDecimal prepaid, LocalDateTime successTime) {
        ServiceOrderPO po = getEntityWithNullCheckForUpdate(serviceOrderId);

        if (po.getOrderStatus() != ServiceOrderStatus.INIT) {
            throw new IllegalArgumentException("订单状态不正确, 不能标记为全额预付费");
        }

        po.setPayStatus(ServiceOrderPayStatus.PRE_PAID);
        po.setCompletedTime(successTime);
        po.setPrepaid(prepaid);

        payOrderService.checkAndUpdateStatus(payOrderId, PayOrderStatus.PAID, PayOrderStatus.UNPAID);

        transferStatusAfterPaid(po);
    }

    @Transactional
    @Override
    public void payRemainingUnpaid(Long serviceOrderId, Long payOrderId) {
        ServiceOrderPO po = getEntityWithNullCheckForUpdate(serviceOrderId);

        if (po.getOrderStatus() != ServiceOrderStatus.SERVICE_COMPLETED) {
            throw new IllegalArgumentException("订单服务未完成, 无法支付");
        }

        if (po.getPayStatus() != ServiceOrderPayStatus.PENDING_PAY) {
            throw new IllegalArgumentException("订单支付状态不为待支付, 无法支付");
        }

        LocalDateTime now = LocalDateTime.now();

        po.setPayStatus(ServiceOrderPayStatus.PAID);
        po.setCompletedTime(now);
        po.setCreditRatingStatus(ServiceOrderCreditRatingStatus.PENDING);
        po.setOrderStatus(ServiceOrderStatus.COMPLETED);

        payOrderService.checkAndUpdateStatus(payOrderId, PayOrderStatus.PAID, PayOrderStatus.UNPAID);

        transferStatusAfterPaid(po);
    }

    @Transactional
    @Override
    public void startService(Long serviceOrderId, Long staffId) {
        ServiceOrderPO po = getEntityWithNullCheckForUpdate(serviceOrderId);

        if (po.getOrderStatus() != ServiceOrderStatus.DISPATCHED_WAITING_SERVICE) {
            throw new IllegalArgumentException("订单状态不正确, 不能标记为服务中");
        }

        po.setServiceStartTime(LocalDateTime.now());
        po.setOrderStatus(ServiceOrderStatus.IN_SERVICE);
    }

    @Transactional
    @Override
    public void dispatchToStaff(Long serviceOrderId, Long staffId) {
        ServiceOrderPO po = getEntityWithNullCheckForUpdate(serviceOrderId);

        if (po.getOrderStatus() != ServiceOrderStatus.PENDING_DISPATCH) {
            throw new IllegalArgumentException("订单状态不正确, 不能指派家政服务员");
        }

        po.setStaffId(staffId);
        po.setOrderStatus(ServiceOrderStatus.DISPATCHED_WAITING_SERVICE);
        po.setUpdatedTime(LocalDateTime.now());
    }

    @Transactional
    @Override
    public void staffConfirm(Long serviceOrderId) {
        ServiceOrderPO po = getEntityWithNullCheckForUpdate(serviceOrderId);

        if (po.getOrderStatus() != ServiceOrderStatus.WAITING_STAFF_CONFIRM) {
            throw new IllegalArgumentException("订单状态不正确, 不能标记为等待服务");
        }

        po.setOrderStatus(ServiceOrderStatus.DISPATCHED_WAITING_SERVICE);
    }

    @Transactional
    @Override
    public void staffReject(Long serviceOrderId) {
        ServiceOrderPO po = getEntityWithNullCheckForUpdate(serviceOrderId);

        if (po.getOrderStatus() != ServiceOrderStatus.WAITING_STAFF_CONFIRM) {
            throw new IllegalArgumentException("订单状态不正确, 不能标记为待重新派单");
        }

        // 取消选择的家政服务员
        po.setStaffId(0L);
        po.setOrderStatus(ServiceOrderStatus.PENDING_REASSIGNMENT);
    }

    @Transactional
    @Override
    public void consumerCancel(Long serviceOrderId) {
        ServiceOrderPO po = getEntityWithNullCheckForUpdate(serviceOrderId);

        if (po.getOrderStatus() != ServiceOrderStatus.INIT
                && po.getOrderStatus() != ServiceOrderStatus.PENDING_DISPATCH
                && po.getOrderStatus() != ServiceOrderStatus.WAITING_STAFF_CONFIRM
                && po.getOrderStatus() != ServiceOrderStatus.PENDING_REASSIGNMENT
                && po.getOrderStatus() != ServiceOrderStatus.DISPATCHED_WAITING_SERVICE) {
            throw new IllegalArgumentException("订单状态不正确, 不能取消");
        }

        po.setOrderStatus(ServiceOrderStatus.CANCELLED_BY_CUSTOMER);
        // 生成退款订单
        ServiceOrderRefundStatus refundStatus = generateRefundOrder(serviceOrderId, po.getCompanyId(), "消费者取消订单, 退款");

        po.setRefundStatus(refundStatus);
    }

    @Transactional
    @Override
    public void companyCancel(Long serviceOrderId) {
        ServiceOrderPO po = getEntityWithNullCheckForUpdate(serviceOrderId);

        if (po.getOrderStatus() != ServiceOrderStatus.PENDING_DISPATCH
                && po.getOrderStatus() != ServiceOrderStatus.WAITING_STAFF_CONFIRM
                && po.getOrderStatus() != ServiceOrderStatus.PENDING_REASSIGNMENT
                && po.getOrderStatus() != ServiceOrderStatus.DISPATCHED_WAITING_SERVICE) {
            throw new IllegalArgumentException("订单状态不正确, 不能取消");
        }

        po.setOrderStatus(ServiceOrderStatus.CANCELLED_BY_COMPANY);

        // 生成退款订单
        ServiceOrderRefundStatus refundStatus = generateRefundOrder(serviceOrderId, po.getCompanyId(), "企业取消订单, 退款");

        po.setRefundStatus(refundStatus);
    }

    @Transactional
    @Override
    public void staffCancel(Long serviceOrderId) {
        ServiceOrderPO po = getEntityWithNullCheckForUpdate(serviceOrderId);

        if (po.getOrderStatus() != ServiceOrderStatus.DISPATCHED_WAITING_SERVICE
                && po.getOrderStatus() != ServiceOrderStatus.IN_SERVICE) {
            throw new IllegalArgumentException("订单状态不正确, 不能取消");
        }

        po.setOrderStatus(ServiceOrderStatus.CANCELLED_BY_STAFF);

        // 生成退款订单
        ServiceOrderRefundStatus refundStatus = generateRefundOrder(serviceOrderId, po.getCompanyId(), "家政员取消订单, 退款");

        po.setRefundStatus(refundStatus);
    }

    @Transactional
    @Override
    public ServiceOrder staffConfirmServiceCompleted(Long serviceOrderId, BigDecimal additionalFee, BigDecimal discountFee) {
        ServiceOrderPO po = getEntityWithNullCheckForUpdate(serviceOrderId);

        if (po.getOrderStatus() != ServiceOrderStatus.IN_SERVICE) {
            throw new IllegalArgumentException("订单状态不正确, 不能标记为服务完成");
        }

        po.setOrderStatus(ServiceOrderStatus.SERVICE_COMPLETED);
        po.setServiceEndTime(LocalDateTime.now());

        // 添加额外费用
        po.setAdditionalFee(additionalFee == null ? BigDecimal.ZERO : additionalFee);
        // 添加减免费用
        po.setDiscountFee(discountFee == null ? BigDecimal.ZERO : discountFee);

        // 维护后续状态
        transferStatusAfterPaid(po);

        return convertQBean(po);
    }

    // 每次支付行为后订单状态迁移进入下一个状态
    private void transferStatusAfterPaid(ServiceOrderPO po) {
        switch (po.getOrderStatus()) {
            case INIT -> {
                if (po.getPayStatus() == ServiceOrderPayStatus.PRE_PAID
                        || po.getPayStatus() == ServiceOrderPayStatus.PART_PRE_PAID
                        || po.getPayStatus() == ServiceOrderPayStatus.POST_PAID) {
                    if (po.getStaffId() > 0) {
                        // 如果已经指定家政服务员 等待确认
                        po.setOrderStatus(ServiceOrderStatus.WAITING_STAFF_CONFIRM);
                    } else {
                        // 否则等待家政公司派单
                        po.setOrderStatus(ServiceOrderStatus.PENDING_DISPATCH);
                    }
                }
            }
            case SERVICE_COMPLETED -> {
                // 维护支付状态
                if (po.getPrice().add(po.getAdditionalFee())
                        .subtract(po.getPrepaid())
                        .subtract(po.getDiscountFee())
                        .compareTo(BigDecimal.ZERO) == 0) {
                    po.setPayStatus(ServiceOrderPayStatus.PAID);
                    // 没有剩余要支付的, 直接变成已完成
                    LocalDateTime now = LocalDateTime.now();
                    po.setCompletedTime(now);
                    po.setCreditRatingStatus(ServiceOrderCreditRatingStatus.PENDING);
                    po.setOrderStatus(ServiceOrderStatus.COMPLETED);
                } else {
                    po.setPayStatus(ServiceOrderPayStatus.PENDING_PAY);
                }
            }
        }

    }

    @Transactional
    @Override
    public void submitCreditRating(Long serviceOrderId, Long userId, String userName, Long score, String comment, List<Long> imageIdList) {
        ServiceOrderPO po = getEntityWithNullCheckForUpdate(serviceOrderId);

        if (po.getOrderStatus() != ServiceOrderStatus.COMPLETED) {
            throw new IllegalArgumentException("订单完成后才能评价");
        }

        if (po.getCreditRatingStatus() != ServiceOrderCreditRatingStatus.PENDING) {
            throw new IllegalArgumentException("订单不是待评价状态");
        }

        // 设置成已提交评价
        po.setCreditRatingStatus(ServiceOrderCreditRatingStatus.SUBMITTED);

        // 发表评价
        val creditRating = creditRatingService.save(
                CreditRatingCreator.builder()
                        .withObjectType(EntityObjectType.SERVICE_ORDER)
                        .withObjectId(serviceOrderId)
                        .withAppraiserId(userId)
                        .withScore(score)
                        .withComment(comment)
                        .withImgIdList(imageIdList)
                        .withStatus(CreditRatingStatus.PENDING)
                        .build()
        );

        creditRatingAuditRecordService.save(CreditRatingAuditRecordCreator.builder()
                .withCreditRatingId(creditRating.getId())
                .withObjectType(EntityObjectType.SERVICE_ORDER)
                .withObjectId(serviceOrderId)
                .withAuditStatus(AuditStatus.NOT_PASS)
                .withUserId(userId)
                .withUserName(userName)
                .withRemark("提交评价")
                .build());
    }

    @Transactional
    @Override
    public void recycleUnpaidTimeout(Long serviceOrderId) {
        ServiceOrderPO po = getEntityWithNullCheckForUpdate(serviceOrderId);

        if (po.getPayStatus() != ServiceOrderPayStatus.UNPAID) {
            throw new IllegalArgumentException("订单支付状态不是未支付, 不能回收");
        }

        po.setPayStatus(ServiceOrderPayStatus.TIMEOUT);
        po.setOrderStatus(ServiceOrderStatus.CANCELLED_UNPAID);

        // 维护支付订单状态
        payOrderService.findByObjectIdAndStatus(EntityObjectType.SERVICE_ORDER, serviceOrderId, PayOrderStatus.UNPAID)
                .forEach(payOrder -> payOrderService.checkAndUpdateStatus(payOrder.getId(), PayOrderStatus.TIMEOUT, PayOrderStatus.UNPAID));
    }

    @Override
    public BigDecimal sumAmountByStatus(ServiceOrderStatus status) {
        BigDecimal amount = dao.sumPriceByOrderStatus(status);
        if (amount == null) {
            return BigDecimal.ZERO;
        }
        return amount;
    }

    @Override
    public Long countOrder(ServiceOrderStatus status, LocalDateTime start, LocalDateTime end) {
        return dao.countByOrderStatusAndCreatedTimeBetween(status, start, end);
    }

    @Override
    public List<ServiceOrder> findByUserIdAndStaffId(Long userId, Long staffId, ServiceOrderStatus status) {
        return convertQBeanToList(dao.findByUserIdAndStaffIdAndOrderStatus(userId, staffId, status));
    }

    @Override
    @Transactional
    public void refreshRefundStatus(Long serviceOrderId, ServiceOrderRefundStatus status) {
        ServiceOrderPO po = getEntityWithNullCheckForUpdate(serviceOrderId);

        po.setRefundStatus(status);
    }

    private ServiceOrderRefundStatus generateRefundOrder(Long serviceOrderId, Long companyId, String refundReason) {

        // 查询所以支付订单
        List<PayOrder> payOrderList = payOrderService.findByObjectId(EntityObjectType.SERVICE_ORDER, serviceOrderId)
                .stream()
                // 过滤已支付和未支付订单
                .filter(it -> PayOrderStatus.PAID.equals(it.getPayOrderStatus()) || PayOrderStatus.UNPAID.equals(it.getPayOrderStatus()))
                .toList();

        ServiceOrderRefundStatus status = payOrderList.size() == 1 && payOrderList.get(0).getPayType().equals(PayType.POST_PAID) ? ServiceOrderRefundStatus.NO_REFUND : ServiceOrderRefundStatus.PROCESSING;

        for (PayOrder it : payOrderList) {
            // 生成退款订单
            if (it.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
                log.info("订单金额小于等于0, 不生成退款订单, payOrderId: {}", it.getId());
                continue;
            }
            RefundOrderCreator.Builder builder = RefundOrderCreator.builder()
                    .withCompanyId(companyId)
                    .withWxTransactionId(String.valueOf(it.getId()))
                    .withWxRefundId(StringUtils.EMPTY)
                    .withObjectType(EntityObjectType.SERVICE_ORDER)
                    .withObjectId(serviceOrderId)
                    .withPayOrderId(it.getId())
                    .withOrderAmount(CurrencyUnitUtils.getCentsAmount(it.getAmount()))
                    .withRefundAmount(CurrencyUnitUtils.getCentsAmount(it.getAmount()))
                    .withUserPayAmount(CurrencyUnitUtils.getCentsAmount(it.getAmount()))
                    .withUserRefundAmount(CurrencyUnitUtils.getCentsAmount(it.getAmount()))
                    .withUserReceivedAccount(StringUtils.EMPTY)
                    .withStatus(RefundStatus.INIT)
                    .withReason(refundReason);

            switch (it.getPayType()) {
                case POST_PAID -> builder
                        .withRefundType(RefundType.NO_REFUND)
                        .withStatus(RefundStatus.NO_REFUND);
                case WECHAT -> builder
                        .withRefundType(RefundType.WECHAT)
                        .withStatus(RefundStatus.PROCESSING);
                case OFFLINE -> builder
                        .withRefundType(RefundType.OFFLINE)
                        .withStatus(RefundStatus.PROCESSING);
            }

            RefundOrder refundOrder = refundOrderService.save(builder.build());

            if (it.getPayType().equals(PayType.WECHAT)) {
                // 微信申请退款
                wechatPayService.applyRefund(it.getId(), refundOrder.getOrderAmount(), refundOrder.getRefundAmount(), refundOrder.getId(), refundReason);
            }
        }

        return status;
    }
}
